/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010.

	This file is part of Maxsi Engine.

	Maxsi Engine is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi Engine is distributed in the hope that it will be useful, but WITHOUT
	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
	for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	MaxsiEngine.dll
	A general purpose C++ library for the Maxsi Technology Project.

	MaxsiString.cpp
	Functions for easier string handling.

******************************************************************************/

#include "MaxsiEngine.h"
#include <math.h>
#ifdef Maxsi_Using_libiconv
#include <iconv.h>
#endif

BeginMaxsiNamespace

MESTR* EvaluateQuote(MESTR* Buffer)
{	
	return EvaluateQuote(Buffer, MESTRLEN(Buffer));
}

MESTR* EvaluateQuote(MESTR* Buffer, size_t OutLen)
{
	MESTR*	Out			=	SubString(0, OutLen, Buffer);
	size_t	Written		=	0;
	bool	Quoted		=	false;

	for ( size_t N = 0; N < OutLen; N++ )
	{
		if ( Buffer[N] == '"' )
		{
			Quoted	=	!Quoted;
		}
		else if ( Buffer[N] == '\\' && N+1 < OutLen && Buffer[N+1] == '"' )
		{
			Out[Written]	=	'"';
			Written			+=	1;
			N++;
		}
		else if ( Buffer[N] == '\\' && N+1 < OutLen && Buffer[N+1] == 'r' )
		{
			Out[Written]	=	'\n';
			Written			+=	1;
			N++;
		}
		else if ( Buffer[N] == '\\' && N+1 < OutLen && Buffer[N+1] == 'n' )
		{
			Out[Written]	=	'\n';
			Written			+=	1;
			N++;
		}
		else if ( Buffer[N] == '\\' && N+1 < OutLen && Buffer[N+1] == 't' )
		{
			Out[Written]	=	'\t';
			Written			+=	1;
			N++;
		}
		else if ( Quoted == false && ( Buffer[N] == '\n' || Buffer[N] == '\r' || Buffer[N] == '\t' || Buffer[N] == ' ' ) )
		{
			break;
		}
		else
		{
			Out[Written]	=	Buffer[N];
			Written			+=	1;
		}
	}

	Out[Written]			=	0;

	return	Out;
}

size_t SkipWhiteSpace(MESTR* Buffer)
{
	return SkipWhiteSpace(Buffer, MESTRLEN(Buffer));
}
size_t SkipWhiteSpace(MESTR* Buffer, size_t BufferLen)
{
	for (size_t I = 0; I < BufferLen; I++)
	{
		if (	Buffer[I] !=	' '
			&&	Buffer[I] !=	'	'
			&&	Buffer[I] !=	'\n'
			&&	Buffer[I] !=	'\r')
		{
			return I;
		}
	}
	return SIZE_MAX;
}

size_t NextWhiteSpace(MESTR* Buffer)
{
	return NextWhiteSpace(Buffer, MESTRLEN(Buffer));
}

size_t NextWhiteSpace(MESTR* Buffer, size_t BufferLen)
{
	for ( size_t I = 0; I < BufferLen; I++ )
	{
		if (	Buffer[I] ==	' '
			||	Buffer[I] ==	'	'
			||	Buffer[I] ==	'\n'
			||	Buffer[I] ==	'\r')
		{
			return I;
		}
	}
	return SIZE_MAX;
}

size_t NextNonQuotedWhiteSpace(MESTR* Buffer)
{
	return NextNonQuotedWhiteSpace(Buffer,MESTRLEN(Buffer));
}

size_t NextNonQuotedWhiteSpace(MESTR* Buffer, size_t BufferLen)
{
	bool	bQuote	=	false;

	for ( size_t I = 0; I < BufferLen; I++ )
	{
		if (	Buffer[I] ==	'"'){bQuote=!bQuote;}
		if (	Buffer[I] ==	'\\'){ I += 2; }
		if (	!bQuote &&			(
				Buffer[I] ==	' '
			||	Buffer[I] ==	'	'
			||	Buffer[I] ==	'\n'
			||	Buffer[I] ==	'\r'))
		{
			return I;
		}
	}
	return SIZE_MAX;
}

size_t NextLine(MESTR* Buffer)
{
	return NextLine(Buffer,MESTRLEN(Buffer));
}

size_t NextLine(MESTR* Buffer, size_t BufferLen)
{
	for (size_t I = 0; I < BufferLen; I++)
	{
		if (	Buffer[I] ==	'\n'
			||	Buffer[I] ==	'\r')
		{
			return I;
		}
	}
	return SIZE_MAX;
}

size_t FindChar(MESTR* Buffer, MESTR CharToFind, size_t BufferLen)
{
	for (size_t I = 0; I < BufferLen; I++)
	{
		if ( Buffer[I] == CharToFind)
		{
			return I;
		}
	}

	return SIZE_MAX;
}

size_t FindCharPrev(MESTR* Buffer, MESTR CharToFind, size_t Begin)
{
	for (size_t I = Begin+1; I>0; I--)
	{
		if ( Buffer[I-1] == CharToFind)
		{
			return I-1;
		}
	}
	return SIZE_MAX;
}

size_t FindString(MESTR* String1, MESTR* String2)
{
	return FindStringEx(String1, MESTRLEN(String1), String2, MESTRLEN(String2));
}

size_t FindStringEx(MESTR* String1, size_t String1Len, MESTR* String2, size_t String2Len)
{
	size_t X = 0, Y = 0;

	if ( String2Len > String1Len ) { return SIZE_MAX; } // String 2 cannot possibly fit into string 1!

	String1Len	-=	String2Len;

	// Loop through X and check at each X if Y follows, if so return X
	for (X = 0; X < String1Len; X++)
	{
		for (Y = 0; Y < String2Len; Y++)
		{
			if ( String1[X+Y] != String2[Y] )
			{
				break; // Nope.
			}
		}
		if ( Y == String2Len )
		{
			return X; // Found at location X
		}
	}
	return SIZE_MAX; // Not Found!

}

char* BuildStringA(size_t NumParameters, ...)
{
	va_list param_pt;

	va_start(param_pt,NumParameters); // Call the setup macro
	
	// First calculate the string lenght

	size_t FinalString_Len = 0;
	char* TMPString = 0;

	for (size_t I = 0 ; I < NumParameters ; I++)
	{
		TMPString = va_arg(param_pt,char*);
		if ( TMPString )
		{
			FinalString_Len+=strlen(TMPString);
		}
	}

	// Allocate the required string
	char* FinalString = new char[FinalString_Len+1];

	if ( FinalString == NULL )
	{
		ME_UNDEFINED_BEHAVIORMESTRC("String Allocation Failed in BuildString()!");
		return NULL;
	}

	FinalString[0] = 0;

	va_end(param_pt); // Closing macro
	va_start(param_pt,NumParameters); // Call the setup macro

	for (size_t I = 0 ; I < NumParameters ; I++)
	{
		TMPString = va_arg(param_pt,char*);
		if ( TMPString )
		{
			strcat_s(FinalString,FinalString_Len+1,TMPString);
		}
	}
	
	return FinalString;
}

wchar_t* BuildStringW(size_t NumParameters, ...)
{
	va_list param_pt;

	va_start(param_pt,NumParameters); // Call the setup macro
	
	// First calculate the string lenght

	size_t FinalString_Len = 0;
	wchar_t* TMPString = 0;

	for (size_t I = 0 ; I < NumParameters ; I++)
	{
		TMPString = va_arg(param_pt,wchar_t*);
		if ( TMPString )
		{
			FinalString_Len+=wcslen(TMPString);
		}
	}

	// Allocate the required string
	wchar_t* FinalString = new wchar_t[FinalString_Len+1];

	if ( FinalString == NULL )
	{
		ME_UNDEFINED_BEHAVIORMESTRC("String Allocation Failed in BuildString()!");
		return NULL;
	}

	FinalString[0] = 0;

	va_end(param_pt); // Closing macro
	va_start(param_pt,NumParameters); // Call the setup macro

	for (size_t I = 0 ; I < NumParameters ; I++)
	{
		TMPString = va_arg(param_pt,wchar_t*);
		if ( TMPString )
		{
			wcscat_s(FinalString,FinalString_Len+1,TMPString);
		}
	}
	
	return FinalString;
}

bool MAXSI_STR_replace(MESTR* input, MESTR* find, MESTR* replace)
{
	if (!input||!find||!replace) { return false; }
	size_t input_Len = MESTRLEN(input);
	size_t find_Len = MESTRLEN(find);
	size_t replace_Len = MESTRLEN(replace);

	return buf_replace(input,input_Len,find,find_Len,replace,replace_Len);
}

bool buf_replace(MESTR* input,size_t input_Len, MESTR* find, size_t find_Len, MESTR* replace, size_t replace_Len)
{
	if (!input||!find||!replace) { return false; }

	if (find_Len != replace_Len)
	{
		return false;
	}

	size_t X = 0, Y = 0;

	// Loop through X and check at each X if Y follows, if so return X
	for (X = 0; X < input_Len; X++)
	{
		for (Y = 0; Y < find_Len; Y++)
		{
			if ( input[X+Y] != find[Y] )
			{
				break; // Nope.
			}
		}
		if ( Y == find_Len )
		{
			memcpy(input+X,replace,find_Len);
		}
	}
	return true; // Done
}

// The algorithm used here is not the fastest but I'll have to do. It's fairly simple and
// as such should be easy to maintain. It attempts to reallocate as little memory as possible.
MESTR*	StringReplace(MESTR* Search, MESTR* Insert, MESTR* Haystack)
{
	// Cache the string lengths!
	size_t	SearchLen	=	MESTRLEN(Search);
	size_t	InsertLen	=	MESTRLEN(Insert);
	size_t	HaystackLen	=	MESTRLEN(Haystack);
	size_t	Searchable	=	HaystackLen-SearchLen+1;
	size_t	X			=	0;
	size_t	Y			=	0;

	// Is what we are looking for longer than what we are looking in?
	if ( HaystackLen < SearchLen )
	{
		// Yep! Just return our Haystack back again, no need to do any working.
		return Haystack;
	}

	// Decide whether we would need memory allocations!
	if ( SearchLen == InsertLen )
	{
		// Yay! What we need to replace is the same length as what we search for!

		// Find any occurrence of what we are looking for
		for ( X = 0; X < Searchable; X++ )
		{
			for ( Y = 0; Y < SearchLen; Y++ )
			{
				if ( Haystack[X+Y] != Search[Y] ) { break; }
			}
			if ( Y == SearchLen )
			{
				// We found one instance of what we are looking for! Go ahead and
				// replace the text in the haystack!
				for ( Y = 0; Y < SearchLen; Y++ )

				{
					Haystack[X+Y] = Insert[Y];
				}
				X += SearchLen-1; // Skip a bit forward in X, we don't wanna be recursive
			}
		}
		
		return Haystack;
	}
	else if ( InsertLen < SearchLen )
	{
		// Yay! What we need to insert is shorter than what we need to replace!

		size_t	Diff	=	SearchLen-InsertLen;

		// Find any occurrence of what we are looking for
		for ( X = 0; X < Searchable; X++ )
		{
			for ( Y = 0; Y < SearchLen; Y++ )
			{
				if ( Haystack[X+Y] != Search[Y] ) { break; }
			}
			if ( Y == SearchLen )
			{
				// We found one instance of what we are looking for! Go ahead and
				// replace the text in the haystack!
				for ( Y = 0; Y < InsertLen; Y++ )
				{
					Haystack[X+Y] = Insert[Y];
				}
				HaystackLen -= Diff; // Make the original string a bit shorter
				for ( size_t N = X+InsertLen; N < HaystackLen; N++ )
				{
					Haystack[N] = Haystack[N+Diff];
				}
				Haystack[HaystackLen] = 0; // Null-terminate the shorter string					
				X += SearchLen-1-Diff; // Skip a bit forward in X, we don't wanna be recursive
			}
		}

		return Haystack;
	}
	else
	{
		// Oh no! We need to allocate more buffer space! First count how much space
		// we need in our new string.

		size_t	RequiredBufferSpace	=	HaystackLen;
		size_t	Occurrences			=	0;

		// Count how many times what we search for occurs in Haystack!
		for ( X = 0; X < Searchable; X++ )
		{
			for ( Y = 0; Y < SearchLen; Y++ )
			{
				if ( Haystack[X+Y] != Search[Y] ) { break; }
			}
			if ( Y == SearchLen )
			{
				Occurrences++;
				X += SearchLen-1; // Skip a bit forward in X, we don't wanna be recursive
			}
		}

		if ( Occurrences == 0 )
		{
			// Haha, would you know that, the string wasn't even found!
			return Haystack;
		}
		else
		{
			// FIXME: THIS CASE IS BROKEN ATM.

			// Figure out how much buffer space is needed!
			if ( InsertLen < SearchLen )
			{
				RequiredBufferSpace	-= (SearchLen-InsertLen)*Occurrences;
			}
			else
			{
				RequiredBufferSpace	+= (InsertLen-SearchLen)*Occurrences;
			}

			// Allocate an appropriate buffer!
			MESTR*		WhatDoYouCallAHaystackWithoutANeedle			=	new MESTR[RequiredBufferSpace+1];
			
			if ( WhatDoYouCallAHaystackWithoutANeedle == NULL ) { return NULL; }

			WhatDoYouCallAHaystackWithoutANeedle[RequiredBufferSpace]	=	0;

			size_t		HP			=	0;

			// Find any occurrence of what we are looking for
			for ( X = 0; X < RequiredBufferSpace; X++ )
			{
				for ( Y = 0; Y < SearchLen; Y++ )
				{
					if ( Haystack[HP+Y] != Search[Y] ) { break; }
				}
				if ( Y == SearchLen )
				{
					// We found one instance of what we are looking for! Go ahead and
					// replace the text in the needle-less haystack!
					for ( Y = 0; Y < InsertLen; Y++ )
					{
						WhatDoYouCallAHaystackWithoutANeedle[X+Y] = Insert[Y];
					}
					X	+=	InsertLen; // Skip a bit forward in X, we don't wanna be recursive
					HP	+=	SearchLen; // Skip a bit forward in HP, we don't wanna be recursive
				}
				
				WhatDoYouCallAHaystackWithoutANeedle[X] = Haystack[HP];
				HP++;
			}

			delete[] Haystack;
			return WhatDoYouCallAHaystackWithoutANeedle;
		}
	}
}	

MESTR* SubString(size_t Start, size_t Length, MESTR* ExtractFrom)
{
	if ( ExtractFrom ==	NULL ) { return NULL; }

	size_t	AvailableLength		=	MESTRLEN(ExtractFrom);

	if ( Start > AvailableLength ) { return NULL; } // Yeah, that's not going to happen.
	if ( Start + Length > AvailableLength ) { Length = AvailableLength-Start; }

	MESTR*	Result		=	new MESTR[Length+1];

	if ( Result )
	{
		memcpy(Result, ExtractFrom+Start, Length);
		Result[Length]	=	0;
	}

	return Result;
}

LINK size_t	HEXTOI			(MESTR* Text)
{
	if (!Text) { return 0; }
	size_t	TextLen		=		MESTRLEN(Text);
	size_t	Exponent	=		0;
	size_t	Out			=		0;
	char	Value		=		0;
	if (!TextLen) { return 0; }
	for (size_t N = TextLen-1; true; N--)
	{
		Value = -1;
		if (Text[N] >= '0' && Text[N] <= '9')
		{
			Value = Text[N] - '0';
		}
		if (Text[N] >= 'a' && Text[N] <= 'f')
		{
			Value = Text[N] - 'a' + 10;
		}
		if (Text[N] >= 'A' && Text[N] <= 'F')
		{
			Value = Text[N] - 'A' + 10;
		}
		if (Value!=-1)
		{
			Out += Value * (int)pow((double)16.0f,(double)Exponent);
			Exponent++;
		}
		if ( N == 0) { break; }
	}
	return Out;
}

#if 0 // Replaced by PrintString

// 32 bit signed integer, base 10
LINK	MESTR*	IToA(int	In)
{
	MESTR*	Result	=	new MESTR[12]; // ceiling(32*ln(2)/ln(10)) + room for a leading '-' + null
	MESTRITOA(In,Result,12,10);
	return	Result;
}

// TODO: There is no function called _uitoa_s!!! Simply pass it on to the 64-bit version
// 32 bit unsigned integer, base 10
LINK	MESTR*	UIToA(unsigned int	In)
{
	return UIToA64((uint64_t)In);
	//MESTR*	Result	=	new MESTR[11]; // ceiling(32*ln(2)/ln(10)) + null
	//_uitoa_s(In,Result,11,10);
	//return	Result;
}

// 64 bit signed integer, base 10
LINK	MESTR*	IToA64(int64_t In)
{
	MESTR*	Result	=	new MESTR[22]; // ceiling(64*ln(2)/ln(10)) + room for a leading '-' + null
	MESTRITOA64(In,Result,22,10);
	return	Result;
	return (MESTR*)RecodeString(From, (BYTE*)Input, InputSize, "wchar_t", sizeof(wchar_t));
}

// 64 bit unsigned integer, base 10
LINK	MESTR*	UIToA64(uint64_t In)
{
	MESTR*	Result	=	new MESTR[21]; // ceiling(64*ln(2)/ln(10)) + null
	MESTRUITOA64(In,Result,21,10);
	return	Result;
}

// double floating point value, base 10
LINK	MESTR*	DToA(double	In, int Digits, int StringLen)
{
	MAXSI_TODO_LOW("This whole DToA function needs to be replaced by something more elegant.");
	
	char*	Result	=	new char[StringLen*sizeof(MESTR)]; // Oh god, this is a lot of bytes.

	_gcvt_s(Result,StringLen,In,Digits);

	if ( strlen(Result) && Result[strlen(Result)-1] == '.' ) { Result[strlen(Result)-1] = 0; }
	
	#ifdef Maxsi_Using_Unicode_Internally

	// If we use multibyte strings, convert it to multibyte strings from ASCII.

	MAXSI_TODO_LOW("The ASCII to wchar_t conversion routine is awful, plus that it depends on that (size_t)0-1 = SIZE_MAX.");

	for ( size_t I = strlen(Result)+1; I != SIZE_MAX; I-- )
	{
		((MESTR*)Result)[I] = (char)Result[I];
	}

	#endif

	return	(MESTR*)Result;
}

#endif


// TODO: Improve, expand, implement, use.
LINK	inline	int		decomchars	(MESTR** data, MESTR* A, MESTR* B, char Na, char Nb)
{
	int		datacache	=	**data;
	return datacache;
}

LINK	char	FromHEXA(char Hex)
{
	if ( Hex >= '0' && Hex <= '9' )
	{
		return Hex-'0';
	}
	else if ( Hex >= 'a' && Hex <= 'f' )
	{
		return Hex-'a'+10;
	}
	else if ( Hex >= 'A' && Hex <= 'F' )
	{
		return Hex-'A'+10;
	}
	return 0;
}

LINK	wchar_t	FromHEXW(wchar_t Hex)
{
	if ( Hex >= '0' && Hex <= '9' )
	{
		return Hex-'0';
	}
	else if ( Hex >= 'a' && Hex <= 'f' )
	{
		return Hex-'a'+10;
	}
	else if ( Hex >= 'A' && Hex <= 'F' )
	{
		return Hex-'A'+10;
	}
	return 0;
}

LINK	MESTR	FromHEXM(MESTR Hex)
{
	if ( Hex >= '0' && Hex <= '9' )
	{
		return Hex-'0';
	}
	else if ( Hex >= 'a' && Hex <= 'f' )
	{
		return Hex-'a'+10;
	}
	else if ( Hex >= 'A' && Hex <= 'F' )
	{
		return Hex-'A'+10;
	}
	return 0;
}


LINK	short	ToHEXA(char In)
{
	short	Result;

	if ( (In%16) < 10 )
	{
		Result	=	'0' + In%16;
	}
	else
	{
		Result	=	'A' + In%16 - 10;
	}
	
	if ( (In/16) < 10 )
	{
		Result	+=	('0' + In/16)*256;
	}
	else
	{
		Result	+=	('A' + In/16)*256;
	}

	return Result;
}

LINK	int	ToHEXW(wchar_t In)
{
	return ToHEXA(In/256)*256+ToHEXA(In%256);
}

LINK	MESTRL	ToHEXM(MESTR In)
{
	#ifdef Maxsi_Using_Unicode_Internally
		return ToHEXW(In);
	#else
		return ToHEXA(In);
	#endif
}

LINK	bool	ShouldCharBeEncodedURL(MESTR In)
{
	if ( 'a' <= In && 'z' >= In )
	{
		return false;
	}
	else if ( 'A' <= In && 'Z' >= In )
	{
		return false;
	}
	else if ( '0' <= In && '9' >= In )
	{
		return false;
	}
	else if ( In == '/' || In == '\\' )
	{
		return false;
	}
	else if ( In == ':' )
	{
		return true;
	}
	else if ( In == '_' || In == '-')
	{
		return false;
	}
	else if ( In == 0 )
	{
		return false;
	}
	return true;
}

LINK	bool	ShouldCharBeEncodedFileName(MESTR In)
{
	if ( 'a' <= In && 'z' >= In )
	{

		return false;
	}
	else if ( 'A' <= In && 'Z' >= In )
	{
		return false;
	}
	else if ( '0' <= In && '9' >= In )
	{
		return false;
	}
	else if ( In == '/' || In == '\\' )
	{
		return true;
	}
	else if ( In == ':' )
	{
		return true;
	}
	else if ( In == '_' || In == '-')
	{
		return false;
	}
	else if ( In == ' ')
	{
		return false;
	}

	else if ( In == 0 )
	{
		return false;
	}
	return true;
}

LINK	bool	DecodeURL(char* URL)
{
	size_t	URLLen	=	strlen(URL);
	size_t	Ahead	=	0;

	for ( size_t I = 0; I < URLLen; I++ )
	{
		if ( URL[I] == '%' )
		{
			if ( I-URLLen < 2 ) { return false; }
			URL[I-Ahead]	=	FromHEXA(URL[I+1])*16+FromHEXA(URL[I+2]);
			Ahead			+=	2;
			I				+=	2;
		}
		else
		{
			URL[I-Ahead]	=	URL[I];
		}			
	}

	URL[URLLen-Ahead]		=	0;

	return true;
}

LINK	char*	EncodeURL(char* URL)
{
	if ( URL == NULL ) { char* Result = new char[1]; Result[0] = 0; return Result; }

	size_t	URLLen	=	strlen(URL);
	size_t	EncLen	=	URLLen;

	for ( size_t I = 0; I < URLLen; I++ )
	{
		if ( ShouldCharBeEncodedURL(URL[I]) )
		{
			EncLen+=2;
		}
	}

	char*	Result	=	new char[EncLen+1];
	EncLen			=	0;

	for ( size_t I = 0; I <= URLLen; I++ )
	{
		if ( ShouldCharBeEncodedURL(URL[I]) )
		{
			short	Hexed		=	ToHEXA(URL[I]);
			Result[I+EncLen]	=	'%';
			Result[I+EncLen+1]	=	(char)(Hexed/256);
			Result[I+EncLen+2]	=	(char)(Hexed%256);
			EncLen+=2;
		}
		else
		{
			Result[I+EncLen]	=	URL[I];
		}
	}

	return Result;
}

LINK	MESTR*	EncodeFileName(MESTR* URL)
{
	if ( URL == NULL ) { MESTR* Result = new MESTR[1]; Result[0] = 0; return Result; }

	size_t	URLLen	=	MESTRLEN(URL);
	size_t	EncLen	=	URLLen;

	for ( size_t I = 0; I < URLLen; I++ )
	{
		if ( ShouldCharBeEncodedFileName(URL[I]) )
		{
			EncLen+=2;
		}
	}

	MESTR*	Result	=	new MESTR[EncLen+1];
	EncLen			=	0;

	for ( size_t I = 0; I <= URLLen; I++ )
	{
		if ( ShouldCharBeEncodedFileName(URL[I]) )
		{
			short	Hexed		=	ToHEXM(URL[I]);
			Result[I+EncLen]	=	'%';
			Result[I+EncLen+1]	=	(char)(Hexed/256);
			Result[I+EncLen+2]	=	(char)(Hexed%256);
			EncLen+=2;
		}
		else
		{
			Result[I+EncLen]	=	URL[I];
		}
	}

	return Result;
}

LINK	size_t	strlen_s		(char* String, size_t MaxLen)
{
	for ( size_t Result = 0; Result < MaxLen; Result++ )
	{
		if ( String[Result] == 0 ) { return Result; }
	}
	return SIZE_MAX; // Not found!
}

LINK	size_t	wcslen_s		(wchar_t* String, size_t MaxLen)
{
	for ( size_t Result = 0; Result < MaxLen; Result++ )
	{
		if ( String[Result] == 0 ) { return Result; }
	}
	return SIZE_MAX; // Not found!
}

//#define Maxsi_Debug_RecodeString

BYTE* RecodeString(const char* From, BYTE* Input, size_t InputSize, const char* To, size_t ToCharSize)
{
	//RecodeString(From, Input, InputSize, To, ToCharSize);

#if defined(Maxsi_Using_libiconv)

	// Create a copy of the Input pointer as char*.
	char*		InputChar				=	(char*)Input;

	//// Find the length of our input and crash if it isn't NUL terminated.
	//size_t		InputLength				=	wcslen(Input);

	// Create a copy of InputSize as calling iconv will change it.
	size_t		InputSizeCopy			=	InputSize;

	// Create a conversion descriptor using libiconv.
	iconv_t		ConversionDescriptor	=	iconv_open(To, From);

	// Check for error conditions.
	if ( ConversionDescriptor == (iconv_t)-1 ) {
#ifdef Maxsi_Debug_RecodeString
PrintOutput("\n\nError: iconv_open failed! Error = %i\n\n", errno);
#endif
return NULL; }

#ifdef Maxsi_Support_Working_Iconv

	PrintOutput("Initial iconv: From %zu %s bytes at %p: \"%S\"\n\n", InputSize, From, InputChar, (wchar_t*)Input);

	// Find the required buffer length for the resulting UTF-8 buffer.
	size_t		ResultLength			=	iconv(ConversionDescriptor, &InputChar, &InputSizeCopy, NULL, NULL);

	PrintOutput("Done with the initial iconv call!\n\n");

	// Check for error conditions.
	if ( ResultLength == (size_t)(-1) ) { PrintOutput(" Error: Initial iconv failed!\n"); iconv_close(ConversionDescriptor); return NULL; }

#else

	MAXSI_TODO_LOW("Seemingly the above call that is outcommented does not return the required length to complete the conversion; intead, for interesting reasons, it just segfaults. I am not sure if it is supposed to do that, someone better ask the maintainer of GNU libiconv! Instead, we just assume the below which should alloce a few too many bytes in most cases.");
	
	size_t		ResultLength			=	InputSize; // Worse case, each input byte produces one output character.

#endif

	// Find the amount of bytes we need in Result.
	size_t		ResultSize				=	(ResultLength + 1) * ToCharSize;


	// Allocate a bufferfull of characters!
	BYTE*		Result					=	new BYTE[(ResultLength + 1)*ToCharSize];

	// Check for error conditions.
	if ( Result == NULL ) { 
#ifdef Maxsi_Debug_RecodeString
	PrintOutput("Error allocating %zu bytes in RecodeString.\n\n", (size_t)(ResultLength + 1)*ToCharSize);
#endif
iconv_close(ConversionDescriptor); return NULL; }

	// Cast Result to char*.
	char*		ResultChar				=	(char*)Result;

	// Reset InputChar as calling iconv changed it.
	/*char**/	InputChar				=	(char*)Input;

	// Reset InputSize as calling iconv changed it.
	/*size_t*/	InputSize				=	InputSizeCopy;

	// Do the actual conversion and check for error conditions.
	if ( iconv(ConversionDescriptor, &InputChar, &InputSize, &ResultChar, &ResultSize) == (size_t)(-1) )
	{
		#ifdef Maxsi_Debug_RecodeString
		PrintOutput("Error: Actual conversion failed in RecodeString from '%s' to '%s' InputChar=%p, InputSize=%zu, ResultChar=%p, ResultSize=%zu, errno=%i\n\n", From, To, InputChar, InputSize, ResultChar, ResultSize, errno);
		#endif
		delete[] Result;
		Result = NULL;	
	}

	iconv_close(ConversionDescriptor);
	return Result;

#elif defined(Maxsi_Support_W32)

	if ( _stricmp(To, "wchar_t") == 0 )
	{
		UINT	CodePage	=	CP_ACP;
		DWORD	Flags		=	0;//MB_PRECOMPOSED;

		if ( _stricmp(From, "UTF-8") == 0 ) { CodePage = CP_UTF8; }

		size_t RequiredLength = MultiByteToWideChar(CodePage, Flags, (char*)Input, InputSize, NULL, 0);

		// Check for error conditions.
		if ( RequiredLength == 0 ) { wprintf(L"RequiredLength = 0, GetLastError()=%u\n", GetLastError()); return NULL; }

		// Allocate a bufferful of the new buffer. Note the above function's result includes the null termination.
		wchar_t* Result = new wchar_t[RequiredLength];

		if ( Result == NULL ) { wprintf(L"Result = NULL\n"); return NULL; }

		size_t ConversionResult = MultiByteToWideChar(CodePage, Flags, (char*)Input, InputSize, Result, RequiredLength);
		
		// Check for error conditions.
		if ( ConversionResult == 0 ) { wprintf(L"ConversionResult = 0\n"); delete[] Result; return NULL; }
		
		return (BYTE*)Result;
	}
	else if ( _stricmp(To, "wchar_t") == 0 )
	{
		UINT	CodePage	=	CP_ACP;

		if ( _stricmp(To, "UTF-8") == 0 ) { CodePage = CP_UTF8; }

		size_t RequiredLength = WideCharToMultiByte(CodePage, 0, (wchar_t*)Input, -1, NULL, 0, NULL, NULL);

		// Check for error conditions.
		if ( RequiredLength == 0 ) { return NULL; }

		// Allocate a bufferful of the new buffer. Note the above function's result includes the null termination.
		BYTE* Result = new BYTE[RequiredLength];

		if ( Result == NULL ) { return NULL; }

		size_t ConversionResult = WideCharToMultiByte(CodePage, 0, (wchar_t*)Input, -1, (char*)Result, RequiredLength, NULL, NULL);
		
		// Check for error conditions.
		if ( ConversionResult == 0 ) { delete[] Result; return NULL; }
		
		return (BYTE*)Result;
	}

	PrintOutput("Unknown text conversion attempted in RecodeString.\n");

	// Unsupported!
	return NULL;

#else

	#error "RecodeString() is not available on your system because only a iconv implementation exists. Please extend this function to your system."

#endif
}

UTF8_t* RecodeStringAsUTF8(const char* From, BYTE* Input, size_t InputSize)
{
	return (UTF8_t*)RecodeString(From, (BYTE*)Input, InputSize, "UTF-8", sizeof(UTF8_t));
}

MESTR* RecodeStringAsMESTR(const char* From, BYTE* Input, size_t InputSize)
{
	return (MESTR*)RecodeString(From, (BYTE*)Input, InputSize, "wchar_t", sizeof(wchar_t));
}

char* WCHAR2CHAR(const wchar_t* Input)
{
	MAXSI_TODO("The system locale is not always UTF-8!");
	return (char*)RecodeString("WCHAR_T", (BYTE*)Input, (wcslen(Input)+1)*sizeof(wchar_t), "UTF-8", sizeof(char));
}

ASCII_t* WCHAR2ASCII(const wchar_t* Input)
{
	return (ASCII_t*)RecodeString("WCHAR_T", (BYTE*)Input, (wcslen(Input)+1)*sizeof(wchar_t), "ASCII", sizeof(ASCII_t));
}

wchar_t* CHAR2WCHAR(const char* Input)
{
	return (wchar_t*)RecodeString("", (BYTE*)Input, (strlen(Input)+1)*sizeof(char), "wchar_t", sizeof(wchar_t));
}

wchar_t* ASCII2WCHAR(const ASCII_t* Input)
{
	return (wchar_t*)RecodeString("ASCII", (BYTE*)Input, (strlen((char*)Input)+1)*sizeof(ASCII_t), "wchar_t", sizeof(wchar_t));
}

UTF8_t* CHAR2UTF8(const char* Input)
{
	MAXSI_TODO("The system locale is not always UTF-8!");
	return (UTF8_t*)RecodeString("UTF-8", (BYTE*)Input, (strlen(Input)+1)*sizeof(char), "UTF-8", sizeof(UTF8_t));
}

#ifdef Maxsi_Use_wbstombs
MaxsiHandle wbstombs_lock	=	NULL;

LINK bool Lockwbstombs()
{
	MAXSI_TODO_LOW("We are creating wbstombs_lock in a non-thread safe manner, if this function isn't called from the main thread in a thread safe manner before any other threads call this function! This might lead to crashes, memory corruption, or just a silly memory leak.");

	if ( wbstombs_lock == NULL ) { wbstombs_lock = ThreadSystem()->CreateCriticalSection(); }
	if ( wbstombs_lock == NULL ) { return false; }

	return ThreadSystem()->EnterCriticalSection(wbstombs_lock);	
}

LINK bool Unlockwbstombs()
{
	return ThreadSystem()->LeaveCriticalSection(wbstombs_lock);	
}
#endif

UTF8_t* WCHAR2UTF8(const wchar_t* Input)
{
	return (UTF8_t*)RecodeString("wchar_t", (BYTE*)Input, (wcslen(Input)+1)*sizeof(wchar_t), "UTF-8", sizeof(UTF8_t));
}

char* UTF82CHAR(const UTF8_t* Input)
{
	return (char*)RecodeString("UTF-8", (BYTE*)Input, (strlen((char*)Input)+1)*sizeof(UTF8_t), "char", sizeof(char));
}

wchar_t* UTF82WCHAR(const UTF8_t* Input)
{
	return (wchar_t*)RecodeString("UTF-8", (BYTE*)Input, (strlen((char*)Input)+1)*sizeof(UTF8_t), "wchar_t", sizeof(wchar_t));
}

StringType_t GuessStringEncoding(BYTE* String, size_t StringSize)
{
	if ( StringSize > 2 && String[0] == 0xEF && String[1] == 0xBB && String[2] == 0xBF ) { return MAXSI_STR_UTF_8; }
	if ( StringSize > 3 && String[0] == 0x00 && String[1] == 0x00 && String[2] == 0xFE && String[3] == 0xFF ) { return MAXSI_STR_UTF_32_BE; }
	if ( StringSize > 3 && String[0] == 0xFF && String[1] == 0xFE && String[2] == 0x00 && String[3] == 0x00) { return MAXSI_STR_UTF_32_LE; }
	if ( StringSize > 1 && String[0] == 0xFE && String[1] == 0xFF ) { return MAXSI_STR_UTF_16_BE; }
	if ( StringSize > 1 && String[0] == 0xFF && String[1] == 0xFE ) { return MAXSI_STR_UTF_16_LE; }

	return MAXSI_STR_SYSTEM_LOCALE; // Not recognized
}

MESTR*	MEMAXSI_STR__ACTUAL_DATE__P	=	NULL;
MESTR	MEMAXSI_STR__ACTUAL_DATE[12];

MESTR*	MEMAXSI_STR__DATE__()
{
	#ifndef Maxsi_Using_Unicode_Internally

		return __DATE__;

	#else

		// There seems to be no preprocessor macro that generates a wide character __DATE__
	
		// Cache the wide date after generating it.
		if ( MEMAXSI_STR__ACTUAL_DATE__P == NULL )
		{
			MEMAXSI_STR__ACTUAL_DATE__P	=	CHAR2MESTR(__DATE__);

			if ( MEMAXSI_STR__ACTUAL_DATE__P )
			{
				if ( MESTRLEN(MEMAXSI_STR__ACTUAL_DATE__P) == 11 )
				{
					memcpy(MEMAXSI_STR__ACTUAL_DATE, MEMAXSI_STR__ACTUAL_DATE__P, sizeof(MESTR)*12);
					delete[]	MEMAXSI_STR__ACTUAL_DATE__P;
					MEMAXSI_STR__ACTUAL_DATE__P	=	MEMAXSI_STR__ACTUAL_DATE;
				}
				else
				{
					delete[]	MEMAXSI_STR__ACTUAL_DATE__P;
				}
			}
		}

		return MEMAXSI_STR__ACTUAL_DATE__P;

	#endif

}

int MESTRCMP(const char* str1, const char* str2)
{
	return strcmp(str1, str2);
}

int MESTRCMP(const wchar_t* str1, const char* str2)
{
	size_t	Offset	=	0;
	while (true)
	{
		if ( str1[Offset] == 0 && str2[Offset] == 0 ) { return 0; }
		if ( str1[Offset] == str2[Offset] ) { continue; }
		if ( str1[Offset] > str2[Offset] ) { return 1; } else { return -1; }
	}
}

int MESTRCMP(const char* str1, const wchar_t* str2)
{
	size_t	Offset	=	0;
	while (true)
	{
		if ( str1[Offset] == 0 && str2[Offset] == 0 ) { return 0; }
		if ( str1[Offset] == str2[Offset] ) { continue; }
		if ( str1[Offset] > str2[Offset] ) { return 1; } else { return -1; }
	}
}

int MESTRCMP(const wchar_t* str1, const wchar_t* str2)
{
	return wcscmp(str1, str2);
}

int MESTRICMP(const char* str1, const char* str2)
{
	return _stricmp(str1, str2);
}

int MESTRICMP(const wchar_t* str1, const char* str2)
{
	MAXSI_TODO("A memory allocation could fail here!");
	wchar_t*	wstr2	=	CHAR2WCHAR(str2);
	int			Result	=	_wcsicmp(str1, wstr2);
	delete[]	wstr2;
	return		Result;
}

int MESTRICMP(const char* str1, const wchar_t* str2)
{
	MAXSI_TODO("A memory allocation could fail here!");
	wchar_t*	wstr1	=	CHAR2WCHAR(str1);
	int			Result	=	_wcsicmp(wstr1, str2);
	delete[]	wstr1;
	return		Result;
}

int MESTRICMP(const wchar_t* str1, const wchar_t* str2)
{
	return _wcsicmp(str1, str2);
}

char* PrintStringFormatToChar(char* Format)
{
	Format	=	BuildStringA(1, Format);

	if ( Format == NULL ) { return NULL; }

	for ( size_t FormatLen = strlen(Format), I = 0, O = 0; I < FormatLen; I++, O++ )
	{
		// Change the contents to fit the platform!
		if ( I < FormatLen - 2 && Format[I] == '%' && Format[I+1] == 'M' && Format[I+2] == 'S' )
		{
#ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=VS.71%29.aspx

			Format[O+1] = 'h';
			Format[O+2] = 's';

			I+=2; O+=2;

#elif defined(__GLIBC__) || defined(Maxsi_Support_MinGW) // http://www.opengroup.org/onlinepubs/000095399/functions/printf.html

			Format[O+1] = 's';
			I+=2; O+=1;

#else

			#error "You need to check how your C++ compiler and C library handles this! The glibc way is mostly standards compliant!"

#endif
		}
		else if ( I < FormatLen - 2 && Format[I] == '%' && Format[I+1] == 'z' && Format[I+2] == 'u' )
		{
#ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=VS.71%29.aspx

			Format[O+1] = 'I';
			Format[O+2] = 'u';

			I+=2; O+=2;

#elif defined(__GLIBC__) || defined(Maxsi_Support_MinGW) // http://www.opengroup.org/onlinepubs/000095399/functions/printf.html

			Format[O+1] = 'z';
			Format[O+2] = 'u';
			I+=2; O+=2;

#else

			#error "You need to check how your C++ compiler and C library handles this! The glibc way is mostly standards compliant!"

#endif	
		}
		else if ( I < FormatLen - 2 && Format[I] == '%' && Format[I+1] == 'M' && Format[I+2] == 'E' )
		{
			Format[O+1] = 'l';
			Format[O+2] = 'u';
			I+=2; O+=2;
		}
		else
		{
			Format[O] = Format[I];
		}

		if ( I == FormatLen-1 ) { Format[O+1] = 0; }
	}

	return Format;
}

wchar_t* PrintStringFormatToWChar(wchar_t* Format)
{
	Format	=	BuildStringW(1, Format);

	if ( Format == NULL ) { return NULL; }

	for ( size_t FormatLen = wcslen(Format), I = 0, O = 0; I < FormatLen; I++, O++ )
	{
		// Change the contents to fit the platform!
		if ( I < FormatLen - 2 && Format[I] == '%' && Format[I+1] == 'M' && Format[I+2] == 'S' )
		{
#ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=VS.71%29.aspx

			Format[O+1] = 'I';
			Format[O+2] = 's';

			I+=2; O+=2;

#elif defined(__GLIBC__) || defined(Maxsi_Support_MinGW) // http://www.opengroup.org/onlinepubs/000095399/functions/printf.html

			Format[O+1] = 'l';
			Format[O+2] = 's';
			I+=2; O+=2;

#else

			#error "You need to check how your C++ compiler and C library handles this! The glibc way is mostly standards compliant!"

#endif
		}
		else if ( I < FormatLen - 2 && Format[I] == '%' && Format[I+1] == 'z' && Format[I+2] == 'u' )
		{
#ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=VS.71%29.aspx

			Format[O+1] = 'I';
			Format[O+2] = 'u';

			I+=2; O+=2;

#elif defined(__GLIBC__) || defined(Maxsi_Support_MinGW) // http://www.opengroup.org/onlinepubs/000095399/functions/printf.html

			Format[O+1] = 'z';
			Format[O+2] = 'u';
			I+=2; O+=2;

#else

			#error "You need to check how your C++ compiler and C library handles this! The glibc way is mostly standards compliant!"

#endif	
		}
		else if ( I < FormatLen - 2 && Format[I] == '%' && Format[I+1] == 'M' && Format[I+2] == 'E' )
		{
			Format[O+1] = 'l';
			Format[O+2] = 'u';
			I+=2; O+=2;
		}
		else
		{
			Format[O] = Format[I];
		}

		if ( I == FormatLen-1 ) { Format[O+1] = 0; }
	}

	return Format;
}

size_t PrintStringVALength(char* Format, va_list ap)
{
#ifdef _MSC_VER
	return _vscprintf(Format, ap);
#elif defined(__GLIBC__) || defined(Maxsi_Support_MinGW)
	return vsnprintf(NULL, 0, Format, ap);
#else
	#error "You need to check how your C++ compiler and C library handles this! The glibc way is mostly standards compliant!"
	return SIZE_MAX;
#endif
}

size_t PrintStringVWLength(char* Format, va_list ap)
{
#ifdef _MSC_VER
	return _vscwprintf(Format, ap);
#elif (defined(__GLIBC__) || defined(Maxsi_Support_MinGW)) && !defined(Maxsi_Support_Buggy_vswprintf)
	return vswprintf(NULL,  Format, ap);
#elif (defined(__GLIBC__) || defined(Maxsi_Support_MinGW)) && defined(Maxsi_Support_Buggy_vswprintf)
	return SIZE_MAX;
#else
	#error "You need to check how your C++ compiler and C library handles this! The glibc way is mostly standards compliant!"
	return SIZE_MAX;
#endif
}

char* PrintStringVA(size_t ResultLength, char* Format, va_list ap)
{
	char* Result = new char[ResultLength+1];

	if ( Result == NULL ) { delete[] Format; return NULL; }
	
#ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=VS.71%29.aspx
	vsnprintf(Result, ResultLength+1, Format, ap);
#elif defined(__GLIBC__) || defined(Maxsi_Support_MinGW) // http://linux.die.net/man/3/snprintf & http://linux.die.net/man/3/swprintf & http://www.opengroup.org/onlinepubs/000095399/functions/printf.html
	vsnprintf(Result, ResultLength+1, Format, ap);
#else
	#error "You need to check how your C++ compiler and C library handles this! The glibc way is mostly standards compliant!"
#endif

	return Result;
}

wchar_t* PrintStringVW(size_t ResultLength, wchar_t* Format, va_list ap)
{
	wchar_t* Result = new wchar_t[ResultLength+1];

	if ( Result == NULL ) { delete[] Format; return NULL; }
	
#ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=VS.71%29.aspx
	vswprintf(Result, ResultLength+1, Format, ap);
#elif defined(__GLIBC__) // http://linux.die.net/man/3/snprintf & http://linux.die.net/man/3/swprintf
	vswprintf(Result, ResultLength+1, Format, ap);
#elif defined(Maxsi_Support_MinGW)
	vswprintf(Result, Format, ap);
#else
	#error "You need to check how your C++ compiler and C library handles this! The glibc way is mostly standards compliant!"
#endif

	return Result;
}

char* PrintStringA(char* Format, ...)
{
	Format = PrintStringFormatToChar(Format);

	if ( Format == NULL ) { return NULL; }

	va_list ap;

	va_start(ap, Format);
	
	size_t ResultLength = PrintStringVALength(Format, ap);
	
	va_end(ap);

	va_start(ap, Format);

	char* Result = PrintStringVA(ResultLength, Format, ap);

	va_end(ap);
	
	delete[] Format;

	return Result;
}

wchar_t* PrintStringW(char* Format, ...)
{
#if defined(Maxsi_Support_Buggy_vswprintf)

	// Yay. The POSIX/ISO C99/glibc definition/implementation of vswprintf is brain-dead.
	// If vswprintf is called with maxlen = 0 it returns -1, while vsnprintf returns the
	// required amount of memory to allocate the string. In fact, according to the glibc
	// source code, only a few lines had to be changed to fix this brain-dead behavior
	// and enable us to do stuff simply. Instead, we have to go through all the hacks
	// beneath:

	// Convert Format to the system locale.
	char* FormatSL = BuildStringA(1, Format);

	// Check if that went well.
	if ( FormatSL == NULL ) { return NULL; }

	// Find the length of our Format String.
	size_t FormatLenSL = strlen(FormatSL);

	// Check if it is possible if Format holds %MS.
	if ( FormatLenSL >= 3 )
	{
		// Change all occurences of %MS to to %ls.
		for ( size_t I = 0; I < FormatLenSL; I++ )
		{
			if ( FormatSL[I] == '%' && FormatSL[I+1] == 'M'  && FormatSL[I+2] == 'S' )
			{
				FormatSL[I+1] = 'l'; 
				FormatSL[I+2] = 's';
				I+=2;
			}
		}
	}

	char* NewFormat = PrintStringFormatToChar(FormatSL);

	delete[] FormatSL; FormatSL = NewFormat;

	if ( FormatSL == NULL ) { return NULL; }

	va_list ap;

	va_start(ap, Format);

	size_t ResultLength = PrintStringVALength(FormatSL, ap);
	
	va_end(ap);

	va_start(ap, Format);

	char* ResultSL = PrintStringVA(ResultLength, FormatSL, ap);

	va_end(ap);
	
	delete[] FormatSL;

	// Check if the print failed.
	if ( ResultSL == NULL ) { return NULL; }

	// Convert back to wchar_t.
	wchar_t* Result = CHAR2WCHAR(ResultSL);

	return Result;	
	
#else

	wchar_t*	FormatWO = CHAR2WCHAR(Format);

	if ( FormatWO == NULL ) { return NULL; }

	wchar_t* FormatW = PrintStringFormatToWChar(FormatWO);

	if ( Format == NULL ) { return NULL; }

	va_list ap;

	va_start(ap, Format);
	
	// Note that this function only returns the proper length (otherwise SIZE_MAX) if
	// Maxsi_Support_Buggy_vswprintf isn't defined and vswprintf acts like I want it to,
	// if we are using glibc. On Windows it works just fine.
	size_t ResultLength = PrintStringVWLength(FormatW, ap);
	
	va_end(ap);

	va_start(ap, Format);

	char* Result = PrintStringVW(ResultLength, FormatW, ap);

	va_end(ap);
	
	delete[] FormatW;
	delete[] FormatWO;

	return Result;

#endif
}

size_t PrintOutputA(char* Format, ...)
{
	Format = PrintStringFormatToChar(Format);

	if ( Format == NULL ) { return SIZE_MAX; }

	va_list ap;

	va_start(ap, Format);

	int	Written = vprintf(Format, ap);

	va_end(ap);

	delete[] Format;

	if ( Written < 0 ) { return SIZE_MAX; }

	return (size_t)Written;
}

size_t PrintOutputW(char* Format, ...)
{
	wchar_t*	FormatWO = CHAR2WCHAR(Format);

	if ( FormatWO == NULL ) { return SIZE_MAX; }

	wchar_t* FormatW = PrintStringFormatToWChar(FormatWO);

	if ( FormatW == NULL ) { return SIZE_MAX; }

	va_list ap;

	va_start(ap, Format);

	int	Written = vwprintf(FormatW, ap);

	va_end(ap);

	delete[] FormatW;
	delete[] FormatWO;

	if ( Written < 0 ) { return SIZE_MAX; }

	return (size_t)Written;
}

EndMaxsiNamespace
